namespace SS.WPF.Framework.Restriction
{
    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;

    public class DynamicTypeHelper
    {
        public static Type CreateEnum(string typeName, IDictionary<string, int> enums)
        {
            // Get the current application domain for the current thread
            var currentDomain = AppDomain.CurrentDomain;

            // Create a dynamic assembly in the current application domain,
            // and allow it to be executed and saved to disk.
            var name = new AssemblyName(typeName);
            var assemblyBuilder = currentDomain.DefineDynamicAssembly(name,
                                                  AssemblyBuilderAccess.RunAndSave);

            // Define a dynamic module in "MyEnums" assembly.
            // For a single-module assembly, the module has the same name as the assembly.
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name,
                                              name.Name + ".dll");

            // Define a public enumeration with the name "MyEnum" and an underlying type of Integer.
            var enumBuilder = moduleBuilder.DefineEnum(string.Format("EnumeratedTypes.{0}", typeName),
                                     TypeAttributes.Public, typeof(int));

            foreach (var nameValue in enums)
            {
                enumBuilder.DefineLiteral(nameValue.Key, nameValue.Value);
            }

            // Create the enum
            var type = enumBuilder.CreateType();

            // Finally, save the assembly
            // assemblyBuilder.Save(name.Name + ".dll");

            return type;
        }

        public static Type CreateEnum(string typeName, params string[] enums)
        {
            IDictionary<string, int> enumDic = new Dictionary<string, int>();
            for (int i = 0; i < enums.Length; i++)
            {
                enumDic[enums[i]] = i;
            }

            return CreateEnum(typeName, enumDic);
        }

        public static object GetValue(Type enumType, string value)
        {
            return Enum.Parse(enumType, value);
        }

        public static Type CompileEnumOnTheFly(string enumName, IDictionary<string, int> enums)
        {
            string code = "enum " + enumName + "{{{0}}}";

            var enumKeyValues = enums.Select(x => x.Key + " = " + x.Value).ToArray();

            var enumBody = string.Join(", ", enumKeyValues);

            code = string.Format(code, enumBody);

            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            var cp = new CompilerParameters { GenerateInMemory = true };

            CompilerResults result = provider.CompileAssemblyFromSource(cp, code);

            Type enumType = result.CompiledAssembly.GetType(enumName);

            return enumType;
        }
    }
}